package com.atguigu.ssyx.activity.service.impl;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.atguigu.ssyx.activity.mapper.SeckillSkuMapper;
import com.atguigu.ssyx.activity.service.SeckillSkuService;
import com.atguigu.ssyx.client.product.ProductFeignClient;
import com.atguigu.ssyx.common.constant.RedisConst;
import com.atguigu.ssyx.common.exception.SsyxException;
import com.atguigu.ssyx.common.result.ResultCodeEnum;
import com.atguigu.ssyx.common.utils.DateUtil;
import com.atguigu.ssyx.model.activity.SeckillSku;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.vo.activity.SeckillSkuQueryVo;
import com.atguigu.ssyx.vo.activity.SeckillSkuVo;
import com.atguigu.ssyx.vo.product.SkuStockLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Windows
 * @description 针对表【seckill_sku(秒杀活动商品关联)】的数据库操作Service实现
 * @createDate 2023-11-16 09:43:42
 */
@Service
public class SeckillSkuServiceImpl extends ServiceImpl<SeckillSkuMapper, SeckillSku>
        implements SeckillSkuService {

    @Autowired
    private ProductFeignClient productFeignClient;

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public IPage<SeckillSku> selectPage(Page<SeckillSku> pageParam, SeckillSkuQueryVo seckillSkuQueryVo) {

        SeckillSku seckillSku = new SeckillSku();
        BeanUtils.copyProperties(seckillSkuQueryVo, seckillSku);
        LambdaQueryWrapper<SeckillSku> seckillSkuLambdaQueryWrapper = new LambdaQueryWrapper<>(seckillSku);
        IPage<SeckillSku> seckillSkuIPage = baseMapper.selectPage(pageParam, seckillSkuLambdaQueryWrapper);
        if (CollectionUtils.isEmpty(seckillSkuIPage.getRecords())) {
            return seckillSkuIPage;
        }

        List<Long> skuIdList = seckillSkuIPage.getRecords().stream().map(SeckillSku::getSkuId).collect(Collectors.toList());
        List<SkuInfo> skuInfoList = productFeignClient.findSkuInfoList(skuIdList);
        Map<Long, SkuInfo> skuIdToSkuInfoMap = skuInfoList.stream().collect(Collectors.toMap(SkuInfo::getId, SkuInfo -> SkuInfo));
        seckillSkuIPage.getRecords().stream().forEach(item -> {
            item.setSkuInfo(skuIdToSkuInfoMap.get(item.getSkuId()));
        });
        return seckillSkuIPage;
    }

    @Override
    public void save(List<SeckillSku> seckillSkuList) {
        this.saveBatch(seckillSkuList);

        // 更新sku为秒杀商品
        List<Long> skuIdList = seckillSkuList.stream().map(SeckillSku::getSkuId).collect(Collectors.toList());

        // productFeignClient.updateSkuType(skuIdList, SkuType.SECKILL);
    }

    /**
     * 将某日的秒杀列表加载到缓存
     *
     * @param date
     */
    @Override
    public void saveSeckillSkuListToCache(String date) {
        List<SeckillSkuVo> seckillSkuVoList = baseMapper.findSeckillSkuListByDate(date);
        if (CollectionUtils.isEmpty(seckillSkuVoList)) {
            return;
        }

        for (SeckillSkuVo seckillSkuVo : seckillSkuVoList) {
            // 将秒杀sku信息放入缓存
            redisTemplate.boundHashOps(RedisConst.SECKILL_SKU_MAP)
                    .put(seckillSkuVo.getSkuId().toString(), seckillSkuVo);

            // 将库存数存放在redis中，防止超卖
            Integer seckillStock = seckillSkuVo.getSeckillStock();
            Long[] skuIdArray = new Long[seckillStock];
            for (Integer i = 0; i < seckillStock; i++) {
                skuIdArray[i] = seckillSkuVo.getSkuId();
            }
            redisTemplate.boundListOps(RedisConst.SECKILL_SKU_LIST + seckillSkuVo.getSkuId())
                    .leftPushAll(skuIdArray);

            //设置过期时间
            //redisTemplate.expire(RedisConst.SECKILL_SKU_MAP, DateUtil.getCurrentExpireTimes(), TimeUnit.SECONDS);
        }
    }

    /**
     * 从缓存中读取秒杀sku信息
     *
     * @param timeName 场次名称
     */
    @Override
    public List<SeckillSkuVo> findSeckillSkuListFromCache(String timeName) {
        List<SeckillSkuVo> seckillSkuVoList = redisTemplate.boundHashOps(RedisConst.SECKILL_SKU_MAP).values();
        if (seckillSkuVoList.size() > 1) {
            seckillSkuVoList.add(seckillSkuVoList.get(0));
            seckillSkuVoList.add(seckillSkuVoList.get(0));
            seckillSkuVoList.add(seckillSkuVoList.get(0));
            seckillSkuVoList.add(seckillSkuVoList.get(0));
        }
        return seckillSkuVoList;
        //return  seckillSkuVoList.stream().filter(seckillSkuVo -> timeName.equals( seckillSkuVo.getTimeName())).collect( Collectors.toList() );
    }

    /**
     * 根据skuId获取秒杀sku信息
     * @param skuId
     * @return
     */
    @Override
    public SeckillSkuVo getSeckillSkuVo(Long skuId) {
        SeckillSkuVo seckillSkuVo = (SeckillSkuVo) redisTemplate.boundHashOps(RedisConst.SECKILL_SKU_MAP).get(skuId.toString());

        if (null == seckillSkuVo) {
            String date = new DateTime().toString("yyyy-MM-dd");
            this.saveSeckillSkuListToCache(date);
        }

        // 场次状态 1-已开枪 2-抢购中 3-即将开枪
        Date currentDate = DateUtil.parseTime(new DateTime().toString("HH:mm:ss"));
        if (DateUtil.dateCompare(seckillSkuVo.getStartTime(), currentDate)) {
            seckillSkuVo.setTimeStaus(1);
        } else {
            seckillSkuVo.setTimeStaus(3);
        }
        return seckillSkuVo;
    }

    /**
     * 检查并减少库存
     * @param skuStockLockVoList
     * @param orderToken
     * @return
     */
    @Transactional
    @Override
    public Boolean checkAndMinusStock(List<SkuStockLockVo> skuStockLockVoList, String orderToken) {

        if (CollectionUtils.isEmpty(skuStockLockVoList)) {
            throw new SsyxException(ResultCodeEnum.DATA_ERROR);
        }

        // 只要存在商品锁定失败，所有锁定成功的商品要解锁库存
        if (skuStockLockVoList.stream().anyMatch(skuStockLockVo -> !skuStockLockVo.getIsLock())) {
            // 遍历所有的商品，检验库存并锁库存，具备原子性
            skuStockLockVoList.forEach(skuStockLockVo -> checkLock(skuStockLockVo));

            // 获取锁定成功的商品，遍历解锁库存
            skuStockLockVoList.stream().filter(SkuStockLockVo::getIsLock).forEach(
                    skuStockLockVo -> baseMapper.rollBackStock(skuStockLockVo.getSkuId(),skuStockLockVo.getSkuNum())
            );
            // 响应锁定状态
            return false;
        }

        // 如果所有商品都锁定成功的情况下，需要缓存锁定信息到redis，以方便将来解锁库存 或者 减库存
        this.redisTemplate.opsForValue().set(RedisConst.SROCK_INFO + orderToken, skuStockLockVoList);
// 锁定库存成功之后，定时解锁库存。
        //this.rabbitTemplate.convertAndSend("ORDER_EXCHANGE", "stock.ttl", orderToken);
        return true;
    }

    // @GmallLock
    private void checkLock(SkuStockLockVo skuStockLockVo){
        SeckillSkuVo seckillSkuVo = this.getSeckillSkuVo(skuStockLockVo.getSkuId());
        if(null == seckillSkuVo) {
            return;
        }

        Integer result = baseMapper.minusStock(seckillSkuVo.getSeckillSkuId(), skuStockLockVo.getSkuNum());
        if (result == 1) {
            skuStockLockVo.setIsLock(true);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rollBackStock(String orderNo) {
        // 获取锁定库存的缓存信息
        List<SkuStockLockVo> skuStockLockVoList = (List<SkuStockLockVo>)this.redisTemplate.opsForValue().get(RedisConst.SROCK_INFO + orderNo);
        if (CollectionUtils.isEmpty(skuStockLockVoList)){
            return ;
        }
        // 回滚库存
        skuStockLockVoList.forEach(skuStockLockVo -> {
            SeckillSkuVo seckillSkuVo = this.getSeckillSkuVo(skuStockLockVo.getSkuId());
            baseMapper.rollBackStock(seckillSkuVo.getSeckillSkuId(), skuStockLockVo.getSkuNum());
        });

        // 回滚库存之后，删除锁定库存的缓存。以防止重复解锁库存
        this.redisTemplate.delete(RedisConst.SROCK_INFO + orderNo);
    }

    @Override
    public boolean removeById(Serializable id) {
        SeckillSku seckillSku = this.getById(id);
        baseMapper.deleteById(id);

        //更新sku为秒杀商品
        List<Long> skuIdList = new ArrayList<>();
        skuIdList.add(seckillSku.getSkuId());
        // productFeignClient.updateSkuType(skuIdList, SkuType.COMMON);
        return false;
    }
}




